﻿using DeviceCommons.DeviceMessages.Enums;

namespace DeviceCommons.Security
{
    public class CrcCalculator
    {
        public static int GetCrcLength(CRCTypeEnum crcType)
        {
            return crcType switch
            {
                CRCTypeEnum.CRC8 => 1,
                CRCTypeEnum.CRC16 => 2,
                CRCTypeEnum.CRC32 => 4,
                _ => 0
            };
        }

        public static byte[] CalculateCrcBytes(CRCTypeEnum CRCType, byte[] data)
        {
            if (CRCType == CRCTypeEnum.None)
                return [];

            return CRCType switch
            {
                CRCTypeEnum.CRC8 => new byte[] { CrcCalculator.ComputeCrc8(data) },
                CRCTypeEnum.CRC16 => BitConverter.GetBytes(CrcCalculator.ComputeCrc16(data)),
                CRCTypeEnum.CRC32 => BitConverter.GetBytes(CrcCalculator.ComputeCrc32(data)),
                _ => []
            };

        }

        public static byte ComputeCrc8(ReadOnlySpan<byte> data)
        {
            byte crc = 0;
            foreach (byte b in data)
            {
                crc ^= b;
                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 0x80) != 0)
                        crc = (byte)((crc << 1) ^ 0x31);
                    else
                        crc <<= 1;
                }
            }
            return crc;
        }

        public static ushort ComputeCrc16(ReadOnlySpan<byte> data)
        {
            ushort crc = 0x0000;
            foreach (byte b in data)
            {
                crc ^= (ushort)(b << 8);
                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 0x8000) != 0)
                        crc = (ushort)((crc << 1) ^ 0x8005);
                    else
                        crc <<= 1;
                }
            }
            return crc;
        }

        public static uint ComputeCrc32(ReadOnlySpan<byte> data)
        {
            uint crc = 0xFFFFFFFF;
            foreach (byte b in data)
            {
                crc ^= b;
                for (int i = 0; i < 8; i++)
                {
                    if ((crc & 1) != 0)
                        crc = (crc >> 1) ^ 0xEDB88320;
                    else
                        crc >>= 1;
                }
            }
            return crc ^ 0xFFFFFFFF;
        }
    }
}
